# coding=utf-8
import gradio as gr
import json
import random
import time
from typing import List, Dict, Any, Tuple, Generator

# 腾讯云API相关导入
import tencentcloud.common.exception.tencent_cloud_sdk_exception as exce
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.credential import Credential
from tencentcloud.hunyuan.v20230901 import hunyuan_client, models


# --- 角色数据类 (与原代码相同) ---
class Character:
    def __init__(self, name, stats_dict, team_name):
        self.name = name
        if not isinstance(team_name, (str, type(None))):
            print(
                f"INTERNAL WARNING: Character '{name}' received non-string/None team_name type: {type(team_name)}, value: {team_name}. Setting team to None.")
            self.team = None
        else:
            self.team = team_name

        self.max_hp = int(stats_dict.get("HP", 100))
        self.current_hp = int(stats_dict.get("current_hp", self.max_hp))
        self.base_atk = int(stats_dict.get("ATK", 10))
        self.base_defe = int(stats_dict.get("DEF", 5))
        self.base_spd = int(stats_dict.get("SPD", 5))
        self.base_spi = int(stats_dict.get("SPI", 10))
        self.skills = stats_dict.get("skills", [])
        self.status_effects = stats_dict.get("status_effects", [])

    @property
    def atk(self):
        return self._get_modified_stat("ATK", self.base_atk)

    @property
    def defe(self):
        return self._get_modified_stat("DEF", self.base_defe)

    @property
    def spd(self):
        return self._get_modified_stat("SPD", self.base_spd)

    @property
    def spi(self):
        return self._get_modified_stat("SPI", self.base_spi)

    def _get_modified_stat(self, stat_name_key, base_value):
        modifier_percentage = 0.0
        for effect in self.status_effects:
            changes = effect.get("stat_changes", {})
            if stat_name_key in changes:
                modifier_percentage += changes[stat_name_key]
        final_value = base_value * (1 + modifier_percentage)
        return max(1, int(final_value))

    def is_defeated(self):
        return self.current_hp <= 0

    def apply_status_effect(self, effect_to_apply):
        existing_effect_idx = -1
        for i, ef in enumerate(self.status_effects):
            if ef.get("source_skill") == effect_to_apply.get("source_skill") and \
                    ef.get("type") == effect_to_apply.get("type") and \
                    ef.get("stat_changes") == effect_to_apply.get("stat_changes"):
                existing_effect_idx = i
                break
        if existing_effect_idx != -1:
            self.status_effects[existing_effect_idx]["duration"] = max(
                self.status_effects[existing_effect_idx].get("duration", 0),
                effect_to_apply.get("duration", 0)
            )
        else:
            self.status_effects.append(effect_to_apply)

    def tick_status_effects(self):
        active_effects = []
        expired_log_entries = []
        for effect in self.status_effects:
            effect["duration"] = effect.get("duration", 1) - 1
            if effect["duration"] > 0:
                active_effects.append(effect)
            else:
                effect_name_part = f"来自“{effect.get('source_skill', '未知技能')}”的" if effect.get(
                    'source_skill') else ""
                stat_change_desc = ""
                if effect.get("stat_changes"):
                    changes_desc_list = []
                    for stat, val in effect.get("stat_changes").items():
                        verb = "提升" if val > 0 else "降低"
                        changes_desc_list.append(f"{stat}{verb}{abs(val * 100):.0f}%")
                    stat_change_desc = f"({', '.join(changes_desc_list)})"
                log_entry = f"{self.name} 身上 {effect_name_part}{effect.get('type', '效果')} {stat_change_desc} 已经结束。"
                expired_log_entries.append(log_entry)
        self.status_effects = active_effects
        return expired_log_entries

    def __str__(self):
        return (f"{self.name} (HP: {self.current_hp}/{self.max_hp}, "
                f"ATK:{self.atk}, DEF:{self.defe}, SPD:{self.spd}, SPI:{self.spi}, "
                f"队伍: {self.team})")


# --- 游戏状态管理类 ---
class GameState:
    def __init__(self):
        # Prompts can be loaded once and stored
        self.argument_system_prompt = '''
        模拟一场由用户输入引发的激烈争吵。角色们会围绕此话题迅速爆发激烈争吵，观点鲜明，火药味十足。
        请严格按照以下规则模拟多人争吵对话:
        1. 格式必须为"角色名: 内容"(中文冒号),每个角色独立一行,禁止嵌套(如"老张: 阿兰: xxx","小黄：为哥：")
        2. 每次响应生成12-20条发言
        3. 每条发言控制在60字内，发言内容要像真人发言年轻人鲜活生动、各种嘲讽调侃，带有情绪。
        4. 确保8个角色都参与(老张、阿兰、阿阳、肯子、陈飞、东哥、小黄、为哥) 每个角色的单次回答不能太长，尽量简短限制在60字以内！角色们必须多轮互动，每个角色人设和口头禅不要混淆。
        5. 每个角色（老张、阿兰、阿阳、肯子、陈飞、东哥、小黄、为哥）都要至少说一句话。鼓励来回讨论互相多次反驳和插嘴。

        角色设定：
        老张：发言以 "老张：" 开头，说话比较逆反，有时具有挑衅，用语古怪不着边际，特别喜欢答非所问。历史虚无主义者、反对一切传统的观点，不喜欢中华文化喜欢东南亚文化，反对民族主义观点，喜欢环游世界尤其是落后地区，喜欢下套套出群友观点后反驳。口头禅："不错" "还不快跪" "卧槽" "胡说八道" "我不太懂" "孤勇者" "你们这群上海瘪三" "无聊" "salabrate" "你们连这都相信？" "不错。" "这些人我不理解" "耽误我这么多年" "偷着乐"。
        阿兰：发言以 "阿兰：" 开头。男生,澳洲曾经留学，为人热情随和，爱好三国、足球相关话题，喜欢妹子的相关话题。在争吵中可能试图和稀泥。口头禅："哦？" "哈哈" "今晚有无" "笑死""国士无双""接着奏乐接着舞"。
        阿阳：发言以 "阿阳：" 开头。极客思维，爱好时政经济相关话题，知识面非常宽广，经常调侃老张的胡言乱语，会夸张模仿老张胡言乱语，经常质疑老张的观点，有英雄情结。口头禅："牛批" "逆天" "老张火星救援" "有点意思" "一眼丁真" "太 low"，还经常使用新三国的经典台词，例如"叉出去","XX之勇不下当年吕布","十倍乃至数十倍","扎聋自己的耳朵"等需要大模型自由发挥。
        肯子：发言以 "肯子：" 开头。家境富裕，律师，极端的汉民族主义、爱国者，爱好历史、种族主义、民族等相关话题，讨厌满清、黑人、白人，喜欢洋妞，军舰爱好者，说话较为极端，极端讨厌 easy girl 的现象，经常带着批判的语气批评社会现象，但观点往往没有深度或者缺乏经济常识，价值观相对保守。口头禅："死啦死啦滴" "木有" "有一说一" "册那" "马鹿" "笑死我" "卧槽" "台巴子、黑鬼、猴子" "什么鬼",还经常叫大家给老婆的公众号点赞。
        陈飞：发言以 "陈飞：" 开头。肯子的同事、朋友，历史知识渊博，现在在澳洲留学。 喜欢苏州，老家宁波，喜欢讨论历史，喜欢留学生话题。口头禅"幽默","666","这什么玩意儿","我靠","我是小白一个"
        东哥：发言以 "东哥：" 开头。阿阳的好朋友，肯子、老张的高中同学。喜欢二次元文化，喜欢日本，说话温和，在医院上班，关注行情交易。口头禅"卧槽""哎""胆子小""软脚虾""不靠谱""梭哈"
        小黄：发言以 "小黄：" 开头。阿阳的前同事，军事发烧友，银行从业人员。 说话温和。口头禅"莽夫","伟大","一败涂地","还是你会玩","可怕","不可能，绝对不可能""蠢蛋""不如ICBC"
        为哥：发言以 "为哥：" 开头。阿阳的高中同学，芯片行业从业者，理科很强非常聪明，说话较为中肯，也喜欢日本动漫和魂类游戏，一般不吵架，在家里练打沙袋。口头禅"啥玩意","原来如此","太TM离谱","肉疼","那确实","亲测有效","装比","我老熟悉了","优秀","我必须把XX干掉"
        '''
        self.char_gen_system_prompt = """
        基于以上对话历史（尤其是角色们的观点和激烈程度），请为上述角色生成战斗属性和3个符合其性格的独特技能，技能名称要适当有趣，也可为武侠风，最好能结合发言内容生成。
        同时，根据他们在争论中的立场和关系，将他们分成两个人数接近的对立的队伍（team1和team2）。
        输出必须是严格的JSON格式，包含 "teams" 和 "characters" 两个主键。

        属性要求：
        - HP (生命值): 整数，范围 80-150
        - ATK (攻击力): 整数，范围 10-20
        - DEF (防御力): 整数，范围 1-10
        - SPD (速度): 整数，范围 1-10 (决定行动顺序)
        - SPI (精神力): 整数，范围 10-20 (可能影响特殊技能效果)
        - team: 字符串，指明角色所属队伍的键名 (只能是 "team1" 或 "team2")
        - current_hp: 整数，初始等于HP
        - status_effects: 空列表 []
        技能要求 (每个角色3个技能，放在 "skills" 列表中，每个技能是一个对象):
        - name: 字符串，技能名称 (例如 "孤勇者", "三国空城计","法治精神","闪电旋风劈")
        - description: 字符串，技能的简短描述
        - type: 字符串，技能类型 ("damage", "buff", "debuff", "heal")
        - target: 字符串，技能目标 ("single_enemy", "all_enemies", "self", "single_ally", "all_allies")
        - accuracy: 整数，命中率 (0-100)
        - power: 整数，技能威力 (用于伤害计算，伤害技能必须有此项，范围10-30)
        - (可选) stat_changes: 对象，用于buff/debuff，键为属性名(如"ATK", "DEF", "SPI", "SPD")，值为变化百分比(如0.2表示增加20%，-0.1表示减少10%)。示例: {"ATK": 0.2, "DEF": -0.1}
        - (可选) duration: 整数，buff/debuff的持续回合数 (一般为2，取值为 2 或 3)
        - (可选) effect_description: 字符串，描述技能的特殊效果，如“使目标混乱1回合”。（此项让LLM自由发挥，Python端可暂不完全实现其逻辑，主要用于播报）
        - (可选) source_skill: 字符串，技能名称，用于状态效果溯源。LLM生成时应与技能 "name" 一致。

        JSON格式示例:
        {
          "teams": { "team1": ["老张", "阿阳", "东哥","小黄"], "team2": ["阿兰", "肯子","陈飞","为哥"] },
          "characters": {
            "老张": {
              "HP": 100, "ATK": 15, "DEF": 5, "SPD": 5, "SPI": 18, "team": "team1", "current_hp": 100, "status_effects": [],
              "skills": [
                {"name": "答非所问", "source_skill": "答非所问", "description": "出其不意的言语攻击", "type": "damage", "target": "single_enemy", "accuracy": 90, "power": 25, "effect_description": "基于精神力造成额外伤害"},
                {"name": "历史虚无", "source_skill": "历史虚无", "description": "散播虚无主义思想，削弱敌人", "type": "debuff", "target": "all_enemies", "accuracy": 80, "stat_changes": {"DEF": -0.15, "SPI": -0.1}, "duration": 2, "effect_description": "降低全体敌人防御和精神"}
              ]
            }
          }
        }
        """
        self.reset()

    def reset(self):
        self.game_phase = "argument"
        self.argument_rounds_done = 0
        self.max_argument_rounds = 2
        self.battle_characters: Dict[str, Character] = {}
        self.battle_teams: Dict[str, List[str]] = {}
        self.battle_turn_order: List[str] = []
        self.current_attacker_idx = 0
        self.chat_history: List[Dict[str, str]] = [{"Role": "system", "Content": self.argument_system_prompt}]
        self.is_responding = False


# --- 全局变量和初始化 ---
try:
    with open('config.json', 'r') as f:
        config = json.load(f)
    SECRET_ID = config['tencent_cloud']['secret_id']
    SECRET_KEY = config['tencent_cloud']['secret_key']
except (FileNotFoundError, KeyError) as e:
    print(f"错误: 无法加载 API 密钥。请确保 'config.json' 文件存在且格式正确。{e}")
    SECRET_ID, SECRET_KEY = None, None


def init_tencent_client():
    if not SECRET_ID or not SECRET_KEY:
        return None
    try:
        credential = Credential(SECRET_ID, SECRET_KEY)

        http_profile = HttpProfile()
        http_profile.req_method = "POST"
        http_profile.req_timeout = 240
        http_profile.endpoint = "hunyuan.tencentcloudapi.com"

        client_profile = ClientProfile()
        client_profile.http_profile = http_profile

        client = hunyuan_client.HunyuanClient(credential, "", client_profile)
        print("DEBUG: API Client initialized successfully.")
        return client
    except Exception as e:
        print(f"DEBUG: API Client initialization failed: {e}")
        return None


CLIENT = init_tencent_client()


# --- 辅助函数：消息格式化 ---
def format_chat_message(sender, message):
    if sender == "system":
        return f"**[系统]**: {message}"
    # 新增：让角色播报看起来更特别
    if sender in ["老张", "阿兰", "阿阳", "肯子", "陈飞", "东哥", "小黄", "为哥"]:
         return f"**[{sender}]**: {message}"
    return message

# --- 核心逻辑函数 ---
def handle_argument_stream_llm(state: GameState, chatbot_history: list) -> Generator[list, None, None]:
    print("DEBUG: Requesting argument stream from LLM...")
    state.is_responding = True
    try:
        req = models.ChatCompletionsRequest()

        history_for_api = []
        # --- 修正点: 使用循环创建 Message 对象 ---
        for msg in state.chat_history:
            if msg["Role"] in ["system", "user", "assistant"]:
                message_obj = models.Message()
                message_obj.Role = msg["Role"]
                message_obj.Content = msg["Content"]
                history_for_api.append(message_obj)

        req.Messages = history_for_api
        req.Model = "hunyuan-turbos-latest"
        # req.Model = "hunyuan-t1-latest"
        req.Stream = True
        req.Temperature = 0.9
        req.TopP = 0.9

        # --- 新增功能：在后台打印请求的具体内容 ---
        print("\n" + "="*50)
        print("--- [DEBUG] Sending API Request (Argument Stream) ---")
        try:
            # 使用SDK对象自带的 to_json_string() 方法，可以清晰地看到所有参数
            print(req.to_json_string())
        except Exception as e:
            print(f"Could not serialize request object for printing: {e}")
        print("="*50 + "\n")
        # --- 新增功能结束 ---


        resp_stream = CLIENT.ChatCompletions(req)
        full_ai_response_for_history = ""

        for chunk_data_wrapper in resp_stream:
            json_string_payload = chunk_data_wrapper.get("data")
            if not json_string_payload or json_string_payload == "[DONE]": continue

            try:
                chunk_data = json.loads(json_string_payload)
                if chunk_data.get("Choices"):
                    delta = chunk_data["Choices"][0].get("Delta", {})
                    new_content_fragment = delta.get("Content", "")
                    if new_content_fragment:
                        full_ai_response_for_history += new_content_fragment
                        # --- 核心修正点: chatbot_history[-1] 是一个列表, 可以被修改 ---
                        chatbot_history[-1][1] = full_ai_response_for_history
                        yield chatbot_history
            except json.JSONDecodeError:
                continue

        if full_ai_response_for_history:
            state.chat_history.append({"Role": "assistant", "Content": full_ai_response_for_history.strip()})
        state.argument_rounds_done += 1

    except Exception as e:
        error_msg = f"处理争吵流时发生错误: {str(e)}"
        print(f"ERROR: {error_msg}")
        # --- 修正点: 使用列表 [] ---
        chatbot_history.append([None, format_chat_message("system", error_msg)])
        yield chatbot_history
    finally:
        state.is_responding = False


def request_character_generation_llm(state: GameState):  # 在 app.py 中，它有更多参数，请保持原样
    """
    请求LLM生成角色数据，使用流式请求来避免60秒超时问题。
    """
    print("DEBUG: Requesting character generation from LLM (using STREAM mode to avoid timeout)...")
    state.is_responding = True
    char_gen_messages = [
        {"Role": "system", "Content": state.char_gen_system_prompt},
        *[msg for msg in state.chat_history if msg["Role"] in ["user", "assistant"]],
        {"Role": "user",
         "Content": "请根据以上对话历史、人设和系统提示，生成每个角色的属性和拥有的3个技能、他们分组到2个队伍的JSON数据。"}
    ]

    try:
        req = models.ChatCompletionsRequest()
        sdk_messages = []
        for msg in char_gen_messages:
            message_obj = models.Message()
            message_obj.Role = msg["Role"]
            message_obj.Content = msg["Content"]
            sdk_messages.append(message_obj)

        req.Messages = sdk_messages
        req.Model = "hunyuan-turbos-latest"

        # --- 核心改动 1: 启用流式输出 ---
        req.Stream = True

        req.Temperature = 0.4

        # 打印请求内容 (保持不变)
        print("\n" + "=" * 50)
        print("--- [DEBUG] Sending API Request (Character Generation - STREAM) ---")
        print(req.to_json_string())
        print("=" * 50 + "\n")

        # --- 核心改动 2: 处理流式响应 ---
        resp_stream = CLIENT.ChatCompletions(req)

        full_json_string = ""
        # 迭代接收所有数据块
        for chunk_data_wrapper in resp_stream:
            json_string_payload = chunk_data_wrapper.get("data")
            if not json_string_payload or json_string_payload == "[DONE]":
                continue

            try:
                chunk_data = json.loads(json_string_payload)
                if chunk_data.get("Choices"):
                    delta = chunk_data["Choices"][0].get("Delta", {})
                    content_fragment = delta.get("Content", "")
                    if content_fragment:
                        # 将所有数据块拼接起来
                        full_json_string += content_fragment
            except json.JSONDecodeError:
                print(f"Warning: Could not decode a stream chunk: {json_string_payload}")
                continue

        # 在接收完所有数据后，返回拼接好的完整JSON字符串
        if full_json_string:
            return True, full_json_string
        else:
            return False, "未能从API的流式响应中获取任何内容。"

    except exce.TencentCloudSDKException as tc_ex:
        return False, f"腾讯云API错误 (角色生成): {str(tc_ex)}"
    except Exception as e:
        return False, f"请求角色数据时发生未知错误: {str(e)}"
    finally:
        state.is_responding = False


def parse_and_setup_characters(state: GameState, json_string: str, chatbot_history: list) -> Tuple[list, str, bool]:
    try:
        if json_string.strip().startswith("```json"):
            json_string = json_string.strip()[7:-3].strip()
        elif json_string.strip().startswith("```"):
            json_string = json_string.strip()[3:-3].strip()

        data = json.loads(json_string)
        state.battle_teams = data.get("teams", {})
        raw_chars_data = data.get("characters", {})
        state.battle_characters.clear()

        # --- 使用字符串拼接代替多次append ---
        character_load_log = "### 角色与队伍配置完成！\n\n"

        # 1. 拼接角色信息
        character_load_log += "**角色列表:**\n"
        for name, stats in raw_chars_data.items():
            state.battle_characters[name] = Character(name, stats, stats.get("team"))
            character_load_log += f"- **{name}**: HP={stats['HP']}, ATK={stats['ATK']}, DEF={stats['DEF']}\n"
            skills_str = ", ".join([f"`{skill['name']}`" for skill in stats.get("skills", [])])
            character_load_log += f"  - *技能*: {skills_str}\n"

        # 2. 拼接队伍信息
        character_load_log += "\n**队伍分配:**\n"
        for team_id, members in state.battle_teams.items():
            valid_members = [m for m in members if m in state.battle_characters]
            state.battle_teams[team_id] = valid_members
            character_load_log += f"- **{team_id}**: {', '.join(valid_members)}\n"

        # --- 一次性添加到聊天记录 ---
        chatbot_history.append([None, character_load_log])

        valid_teams_with_members = {tid: mem for tid, mem in state.battle_teams.items() if mem}
        if len(valid_teams_with_members) < 2:
            chatbot_history.append(
                [None, format_chat_message("system", "错误：未能形成至少两个有效的队伍，无法开始战斗。")])
            state.game_phase = "argument"
            return chatbot_history

        state.game_phase = "char_setup_done"
        return chatbot_history

    except (json.JSONDecodeError, Exception) as e:
        error_msg = f"设置角色时出错: {str(e)}"
        chatbot_history.append([None, format_chat_message("system", error_msg)])
        state.game_phase = "argument"
        return chatbot_history


# --- 战斗逻辑 (与之前相同, 但确保其调用的 chatbot_history 是可变的) ---
def _ai_choose_action(attacker: Character, all_characters: Dict[str, Character]) -> Tuple[dict, list]:
    use_basic_attack_probability = 1 / 3
    chosen_action = None
    targets = []

    if random.random() < use_basic_attack_probability or not attacker.skills:
        chosen_action = {"name": "普通攻击", "type": "basic_attack", "target": "single_enemy", "accuracy": 95}
    else:
        chosen_action = random.choice(attacker.skills)

    action_target_type = chosen_action.get("target")

    if action_target_type == "self":
        targets = [attacker]
    elif action_target_type in ["single_enemy", "all_enemies"]:
        opponents = [c for c in all_characters.values() if c.team != attacker.team and not c.is_defeated()]
        if not opponents: return None, []
        if action_target_type == "single_enemy":
            targets = [random.choice(opponents)]
        else:
            targets = opponents
    elif action_target_type in ["single_ally", "all_allies"]:
        allies = [c for c in all_characters.values() if c.team == attacker.team and not c.is_defeated()]
        if not allies: return None, []
        if action_target_type == "single_ally":
            targets = [random.choice(allies)]
        else:
            targets = allies

    return chosen_action, targets


def execute_skill(attacker: Character, target_char: Character, skill_or_action: dict) -> list:
    log_entries = []
    action_name = skill_or_action.get("name", "未知行动")
    action_type = skill_or_action.get("type", "unknown")

    # --- 修改播报格式 ---
    # 不再使用 [系统]，而是使用攻击者自己的名字
    sender = attacker.name

    action_header = f"{attacker.name} ({attacker.current_hp} HP) 对 {target_char.name} ({target_char.current_hp} HP) 使用 **【{action_name}】**"
    log_entries.append((sender, action_header))  # 返回一个元组 (发送者, 消息)

    is_hit = random.randint(1, 100) <= skill_or_action.get("accuracy", 90)
    if not is_hit:
        log_entries.append((sender, "  ↳ 未命中！(MISS)"))
        return log_entries

    if action_type == "basic_attack" or action_type == "damage":
        power = skill_or_action.get("power", 10)
        damage = (attacker.atk + power) - target_char.defe
        damage = max(1, int(damage * random.uniform(0.9, 1.1)))
        target_char.current_hp -= damage
        log_entries.append((sender,
                            f"  ↳ 造成 {damage} 点伤害！{target_char.name} 剩余HP: {target_char.current_hp}/{target_char.max_hp}"))
        if target_char.is_defeated():
            log_entries.append((sender, f"  ↳ **{target_char.name} 被击败了！**"))

    elif action_type in ["buff", "debuff"]:
        stat_changes = skill_or_action.get("stat_changes", {})
        duration = skill_or_action.get("duration", 1)
        effect_data = {"type": action_type, "stat_changes": stat_changes, "duration": duration + 1,
                       "source_skill": action_name}
        target_char.apply_status_effect(effect_data)

        desc = [f"{stat}{'提升' if val > 0 else '降低'}{abs(val * 100):.0f}%" for stat, val in stat_changes.items()]
        if desc:
            log_entries.append((sender, f"  ↳ {target_char.name} 的 {', '.join(desc)}，持续 {duration} 回合。"))
    return log_entries


def check_battle_end(state: GameState) -> str:
    active_members_per_team = {}
    for team_id in state.battle_teams:
        active_members_per_team[team_id] = [name for name in state.battle_teams[team_id] if
                                            not state.battle_characters[name].is_defeated()]

    living_teams = [team_id for team_id, members in active_members_per_team.items() if members]

    if len(living_teams) <= 1:
        if len(living_teams) == 1:
            winner_team_id = living_teams[0]
            winner_members_names = ', '.join(state.battle_teams.get(winner_team_id, []))
            return f"**！！！战斗结束！！！**\n队伍 **{winner_team_id}** ({winner_members_names}) 获得了胜利！"
        else:
            return "**！！！战斗结束！！！**\n所有人都被击败了，真是惨烈的平局..."
    return ""


def run_battle_flow(state: GameState, chatbot_history: list) -> Generator[list, None, None]:
    state.game_phase = "battle"
    # --- 修正点: 使用列表 [] ---
    chatbot_history.append([None, format_chat_message("system", "战斗正式开始！")])

    all_fighters = [c for team_members in state.battle_teams.values() for c in team_members if
                    c in state.battle_characters]
    active_fighters = [state.battle_characters[name] for name in all_fighters]
    active_fighters.sort(key=lambda c: c.spd, reverse=True)
    state.battle_turn_order = [char.name for char in active_fighters]
    state.current_attacker_idx = 0

    # --- 修正点: 使用列表 [] ---
    chatbot_history.append([None, format_chat_message("system", "行动顺序：" + " -> ".join(state.battle_turn_order))])
    yield chatbot_history
    time.sleep(1.5)

    while True:
        attacker_name = state.battle_turn_order[state.current_attacker_idx]
        attacker = state.battle_characters.get(attacker_name)

        if attacker and not attacker.is_defeated():
            chatbot_history.append([None, format_chat_message("system", f"\n轮到 **{attacker.name}** 行动...")])
            yield chatbot_history
            time.sleep(0.8)

            chosen_skill, targets = _ai_choose_action(attacker, state.battle_characters)

            if chosen_skill and targets:
                for target_char in targets:
                    if target_char.is_defeated(): continue
                    # --- 这里接收新的日志格式 ---
                    log_entries_with_sender = execute_skill(attacker, target_char, chosen_skill)
                    for sender, entry in log_entries_with_sender:
                        # 使用 format_chat_message 来格式化角色播报
                        chatbot_history.append([None, format_chat_message(sender, entry)])
                    yield chatbot_history
                    time.sleep(0.8)

            end_message = check_battle_end(state)
            if end_message:
                # --- 修正点: 使用列表 [] ---
                chatbot_history.append([None, format_chat_message("system", end_message)])
                state.game_phase = "battle_over"
                yield chatbot_history
                break

        state.current_attacker_idx = (state.current_attacker_idx + 1) % len(state.battle_turn_order)


# --- Gradio 主交互函数 (重构优化版) ---

def handle_interaction(user_input, chatbot_history, state):
    """
    核心交互逻辑生成器，根据游戏阶段处理输入并逐步yield UI更新。
    注意：此函数不再负责清空输入框。
    """
    if state.is_responding:
        # 如果仍在处理上一个请求，则不做任何事
        yield chatbot_history, state, gr.update(), gr.update()
        return

    # --- 阶段 1: 争吵 ---
    if state.game_phase == "argument":
        if user_input:
            chatbot_history.append([user_input, None])
            state.chat_history.append({"Role": "user", "Content": user_input})
        else:
            if state.argument_rounds_done == 0:
                chatbot_history.append([None, format_chat_message("system", "请输入一个引发争吵的话题开始。")])
                yield chatbot_history, state, gr.update(interactive=True), gr.update(interactive=True)
                return
            chatbot_history.append(["(示意继续争吵...)", None])

        # 准备AI响应
        chatbot_history.append([None, ""])  # 为AI响应占位
        yield chatbot_history, state, gr.update(interactive=False), gr.update(interactive=False)

        # 流式处理AI争吵
        for updated_history in handle_argument_stream_llm(state, chatbot_history):
            yield updated_history, state, gr.update(interactive=False), gr.update(interactive=False)

        # 检查是否该生成角色了
        if state.argument_rounds_done >= state.max_argument_rounds:
            state.game_phase = "char_setup_request"
            chatbot_history.append(
                [None, format_chat_message("system", "争论已足够激烈！将开始备战，生成角色属性和队伍...")])
            # 更新UI状态为“生成中”
            yield chatbot_history, state, \
                gr.update(value="AI生成中...", interactive=False), \
                gr.update(value="生成中...", interactive=False)

            # 请求角色生成 (这是个阻塞操作)
            success, result = request_character_generation_llm(state)

            if success:
                # 解析并显示角色信息
                chatbot_history = parse_and_setup_characters(state, result, chatbot_history)
                # 更新UI状态为“准备战斗”
                yield chatbot_history, state, \
                    gr.update(value="", placeholder="角色已生成，请点击开始战斗", interactive=False), \
                    gr.update(value="开始战斗！", interactive=True)
            else:
                # 生成失败，退回争吵阶段
                chatbot_history.append([None, format_chat_message("system", f"角色生成失败: {result}")])
                state.game_phase = "argument"
                yield chatbot_history, state, \
                    gr.update(placeholder="出现错误，请重试或输入新话题", interactive=True), \
                    gr.update(value="发表观点", interactive=True)
        else:
            # 争吵继续，恢复UI
            yield chatbot_history, state, gr.update(interactive=True), gr.update(interactive=True)

    # --- 阶段 2: 准备战斗 (点击“开始战斗”) ---
    elif state.game_phase == "char_setup_done":
        # 更新UI为“战斗中”
        yield chatbot_history, state, \
            gr.update(interactive=False), \
            gr.update(value="战斗中...", interactive=False)

        # 逐回合进行战斗
        for updated_history in run_battle_flow(state, chatbot_history):
            # 战斗结束后，run_battle_flow会将game_phase设为"battle_over"
            if state.game_phase == "battle_over":
                # 更新UI为“战斗结束/可重启”
                yield updated_history, state, \
                    gr.update(value="", placeholder="战斗结束！输入新话题以重新开始...", interactive=True), \
                    gr.update(value="重新开始", interactive=True)
            else:
                # 战斗仍在进行，保持UI禁用
                yield updated_history, state, gr.update(interactive=False), gr.update(interactive=False)

    # --- 阶段 3: 战斗结束 (点击“重新开始”) ---
    elif state.game_phase == "battle_over":
        state.reset()
        chatbot_history = [[None, format_chat_message("system", "游戏已重置。请输入新的话题开始。")]]
        # 更新UI为初始状态
        yield chatbot_history, state, \
            gr.update(value="", placeholder="例如：讨论一下XXX...", interactive=True), \
            gr.update(value="发表观点", interactive=True)


def handle_and_clear(user_input, chatbot_history, state):
    """
    一个包装函数，调用主交互逻辑并负责清空输入框。
    这是推荐在Gradio事件监听中使用的函数。
    """
    # 存储最后一次的UI更新状态
    final_outputs = None

    # 逐步执行主逻辑生成器
    for outputs in handle_interaction(user_input, chatbot_history, state):
        final_outputs = outputs
        yield final_outputs

    # 在主逻辑完成后，如果成功，则额外发送一次UI更新来清空输入框
    if final_outputs:
        # 将返回的元组转换为列表，以便修改
        final_outputs_list = list(final_outputs)

        # 假设第3个输出 (索引为2) 是 user_input_textbox 的更新
        # 我们用一个空字符串 "" 来清空它
        final_outputs_list[2] = ""

        # 再次yield最终的、包含清空指令的UI状态
        yield tuple(final_outputs_list)


def clear_input():
    return ""


# --- Gradio UI 布局 ---
with gr.Blocks(theme=gr.themes.Soft(), title="历史讨论群全武行") as demo:
    game_state = gr.State(GameState())

    gr.Markdown("# 历史讨论群全武行\n## A Text-based Battle Arena powered by LLM")

    chatbot = gr.Chatbot(
        # --- 修正点: 初始值使用列表的列表 [[]] ---
        value=[[None, format_chat_message("system", "请输入一个引发争吵的话题开始。")]],
        label="聊天与战斗日志",
        height=700,
        avatar_images=(None, "https://s2.loli.net/2024/04/09/pWU7fIYk9yClwJa.png")  # (可选) 为AI设置一个头像
    )

    with gr.Row():
        user_input_textbox = gr.Textbox(
            label="输入你的观点",
            placeholder="例如：讨论一下XXX...",
            scale=4
        )
        send_button = gr.Button("发表观点", variant="primary", scale=1)


    # --- 事件监听 ---
    # 将清空输入的逻辑从 .then() 移到主函数末尾，简化流程
    def handle_and_clear(user_input, chatbot_history, state):
        # 这个生成器会逐步产生输出
        for outputs in handle_interaction(user_input, chatbot_history, state):
            # 在每次 yield 之间，我们不清空输入框
            yield outputs
        # 当 handle_interaction 完成后 (即生成器结束时)，我们才清空输入框
        # 返回的最后一次状态需要包含清空后的输入框
        final_chatbot, final_state, final_input_update, final_button_update = outputs
        yield final_chatbot, final_state, "", final_button_update


    send_button.click(
        fn=handle_and_clear,
        inputs=[user_input_textbox, chatbot, game_state],
        outputs=[chatbot, game_state, user_input_textbox, send_button]
    )

    user_input_textbox.submit(
        fn=handle_and_clear,
        inputs=[user_input_textbox, chatbot, game_state],
        outputs=[chatbot, game_state, user_input_textbox, send_button]
    )

if __name__ == "__main__":
    if not CLIENT:
        print("\n错误：API客户端未能初始化，无法启动应用。请检查您的`config.json`文件。")
    else:
        demo.launch()